﻿using SYMetro.Domain.Modal.Membership;
using SYMetro.Domain.Repository.Membership;
using SYMetro.ServiceContract.Membership;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YiDa.Domain.Repositories;
using YiDa.Infrastructure.Caching;
using SYMetro.DataObjects;
using AutoMapper;
using YiDa.Domain.Specifications;
using System.ServiceModel;
using YiDa.Domain;

namespace SYMetro.Application.Membership
{
    public class RoleService : ApplicationService,IRoleService
    {
        IRoleRepository _roleRepository = null;
        IUserRepository _userRepository = null;

        public RoleService(IRepositoryContext context, 
            IRoleRepository roleRepository,
            IUserRepository userRepository)
            : base(context)
        {
            _roleRepository = roleRepository;
            _userRepository = userRepository;            
        }        
 
        public RoleDataObjectList CreateRoles(RoleDataObjectList roles)
        {
            return PerformCreateObjects<RoleDataObjectList, RoleDataObject, Role>(roles, _roleRepository);
        }

        public void DeleteRole(IDList ids, bool throwOnPopulatedRole)
        {
            try
            {
                ids.ForEach(i =>
                {
                    var role = _roleRepository.GetByKey(Guid.Parse(i));
                    if (throwOnPopulatedRole)
                    {
                        if (role.Users.Count > 0)
                        {
                            throw new DomainException("角色:{0}中包含用户", role.RoleName);
                        }
                    }
                    _roleRepository.Remove(role);
                });
                Context.Commit();
            }
            catch (Exception ex)
            {
                throw new FaultException<FaultData>(FaultData.CreateFromException(ex), FaultData.CreateFaultReason(ex));
            }
        }


        public bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            bool result = false;
            var role = _roleRepository.Find(Specification<Role>.Eval(r => r.RoleName == roleName));
            if (throwOnPopulatedRole && role!=null)
            {
                if (role.Users.Count > 0)
                {
                    throw new DomainException("角色:{0}中包含用户", role.RoleName);
                }
            }
            if (role != null)
            {
                _roleRepository.Remove(role);

                Context.Commit();
                result = true;
            }
            return result;
        }

        public void RemoveUsersFromRoles(string[] userNames, string[] roleNames)
        {
            var roles = _roleRepository.FindAll(r=>roleNames.Contains(r.RoleName),r=>r.Users);
            foreach (var role in roles)
            {
                role.Users.Where(u => userNames.Contains(u.UserName)).
                    ToList().
                    ForEach(
                        u=>role.Users.Remove(u)
                    );

                _roleRepository.Update(role);                
            }
            Context.Commit();
        }

        public void AddUsersToRoles(string[] userNames, string[] roleNames)
        {
            var roles = _roleRepository.FindAll(r => roleNames.Contains(r.RoleName), r => r.Users);
            foreach (var role in roles)
            {
                foreach (var userName in userNames)
                {
                    if (role.Users.Select(u => u.LoweredUserName).Contains(userName.ToLower()))
                    {
                        continue;
                    }
                    var user = _userRepository.Find(Specification<User>.Eval(u=>u.UserName==userName));
                    if (user == null)
                    {
                        user = new User()
                        {
                            ID = Guid.NewGuid(),
                            UserName = userName,
                            LoweredUserName = userName.ToLower(),
                            IsAnonymous = false,
                            LastActivityDate = DateTime.UtcNow
                        };
                        _userRepository.Add(user);
                    }
                    role.Users.Add(user);
                    _roleRepository.Update(role);
                }
            }
            Context.Commit();
        }

        public bool RoleExists(string roleName)
        {
            var role = _roleRepository.Find(Specification<Role>.Eval(r=>r.RoleName==roleName));
            return role != null;
        }

        public UserDataObjectList FindUsersInRole(string roleName, string usernameToMatch)
        {
            var result = new UserDataObjectList();
            var role = _roleRepository.Find(Specification<Role>.Eval(r => r.RoleName == roleName),r=>r.Users);
            role.Users.Where(u => u.UserName.Contains(usernameToMatch)).
                ToList().ForEach(u => {
                    result.Add(Mapper.Map<User, UserDataObject>(u));
                });
            return result;
        }

        public RoleDataObjectList GetAllRoles()
        {
            var roles = _roleRepository.FindAll(r=>r.Users);
            var result = new RoleDataObjectList();

            roles.ToList().ForEach(
                    r => result.Add(Mapper.Map<Role, RoleDataObject>(r))
                );

            return result;
        }

        public UserDataObjectList GetUsersInRole(string roleName)
        {
            var result = new UserDataObjectList();
            var role = _roleRepository.Find(Specification<Role>.Eval(r => r.RoleName == roleName), r => r.Users);
            role.Users.ForEach(u =>
                {
                    result.Add(Mapper.Map<User, UserDataObject>(u));
                });
            return result;
        }

        public bool IsUserInRole(string userName, string roleName)
        {
            bool result = false;

            List<string> roleNames = new List<string>();
            var roles = _roleRepository.GetRolesByUserIn(userName);
            roles.ToList().ForEach(r =>
            {
                roleNames.AddRange(_roleRepository.GetAncestorRoleNames(r.ID));
            });
            if (roleNames.Contains(roleName))
            {
                result = true;
            }

            return result;
        }

        public RoleDataObjectList GetRolesForUser(string userName)
        {
            var result = new RoleDataObjectList();

            List<string> roleNames = new List<string>();
            var roles = _roleRepository.GetRolesByUserIn(userName);
            roles.ToList().ForEach(r =>
            {
                roleNames.AddRange(_roleRepository.GetAncestorRoleNames(r.ID));
            });
            roleNames = roleNames.Distinct().ToList();
            var rolesAll = _roleRepository.FindAll(Specification<Role>.Eval(r => roleNames.Contains(r.RoleName)));            
            rolesAll.ToList().ForEach(                    
                    r => result.Add(Mapper.Map<Role,RoleDataObject>(r))
                );
            return result;
        }

        public RoleDataObject GetRoleByName(string roleName)
        {
            var role = _roleRepository.Find(Specification<Role>.Eval(r => r.RoleName == roleName), r => r.Users);
            return Mapper.Map<Role, RoleDataObject>(role);
        }

        public RoleDataObject GetRoleByID(Guid id)
        {
            var role = _roleRepository.Find(Specification<Role>.Eval(r => r.ID == id), r => r.Users);
            return Mapper.Map<Role, RoleDataObject>(role);
        }

        public RoleDataObjectList GetRolesByParentId(Guid id)
        {
            RoleDataObjectList result = new RoleDataObjectList();
            IEnumerable<Role> roles = null;
            if (id == Guid.Empty)
            {
                roles = _roleRepository.FindAll(Specification<Role>.Eval(r => r.Parent==null || r.Parent.ID==Guid.Empty), r => r.Users);
            }
            else
            {
                roles=_roleRepository.FindAll(Specification<Role>.Eval(r => r.Parent.ID == id), r => r.Users);
            }
            roles.ToList().ForEach(r => {
                result.Add(Mapper.Map<Role, RoleDataObject>(r));
            });
            return result;
        }

        public UserDataObjectList FindUsersInRole(Guid roleId)
        {
            var r = GetRoleByID(roleId);
            return r.Users;
        }


    }
}
